home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / chpass / chpass.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-07-15  |  11.0 KB  |  483 lines

  1. /*
  2.  * Copyright (c) 1988 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted
  6.  * provided that the above copyright notice and this paragraph are
  7.  * duplicated in all such forms and that any documentation,
  8.  * advertising materials, and other materials related to such
  9.  * distribution and use acknowledge that the software was developed
  10.  * by the University of California, Berkeley.  The name of the
  11.  * University may not be used to endorse or promote products derived
  12.  * from this software without specific prior written permission.
  13.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  14.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  15.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  16.  */
  17.  
  18. #ifndef lint
  19. char copyright[] =
  20. "@(#) Copyright (c) 1988 The Regents of the University of California.\n\
  21.  All rights reserved.\n";
  22. #endif /* not lint */
  23.  
  24. #ifndef lint
  25. static char sccsid[] = "@(#)chpass.c    5.10 (Berkeley) 3/26/89";
  26. #endif /* not lint */
  27.  
  28. #include <sys/param.h>
  29. #include <sys/file.h>
  30. #include <sys/stat.h>
  31. #include <sys/signal.h>
  32. #include <sys/time.h>
  33. #include <sys/resource.h>
  34. #include <pwd.h>
  35. #include <errno.h>
  36. #include <stdio.h>
  37. #include <ctype.h>
  38. #include <chpass.h>
  39. #include <strings.h>
  40.  
  41. char e1[] = ": ";
  42. char e2[] = ":,";
  43.  
  44. int p_change(), p_class(), p_expire(), p_gecos(), p_gid(), p_hdir();
  45. int p_login(), p_passwd(), p_shell(), p_uid();
  46.  
  47. struct entry list[] = {
  48.     { "Login",        p_login,  1,   5, e1,   },
  49.     { "Password",        p_passwd, 1,   8, e1,   },
  50.     { "Uid",        p_uid,    1,   3, e1,   },
  51.     { "Gid",        p_gid,    1,   3, e1,   },
  52.     { "Class",        p_class,  1,   5, e1,   },
  53.     { "Change",        p_change, 1,   6, NULL, },
  54.     { "Expire",        p_expire, 1,   6, NULL, },
  55. #define    E_NAME        7
  56.     { "Full Name",        p_gecos,  0,   9, e2,   },
  57. #define    E_BPHONE    8
  58.     { "Office Phone",    p_gecos,  0,  12, e2,   },
  59. #define    E_HPHONE    9
  60.     { "Home Phone",        p_gecos,  0,  10, e2,   },
  61. #define    E_LOCATE    10
  62.     { "Location",        p_gecos,  0,   8, e2,   },
  63.     { "Home directory",    p_hdir,   1,  14, e1,   },
  64.     { "Shell",        p_shell,  0,   5, e1,   },
  65.     { NULL, 0, },
  66. };
  67.  
  68. uid_t uid;
  69.  
  70. main(argc, argv)
  71.     int argc;
  72.     char **argv;
  73. {
  74.     extern int errno, optind;
  75.     extern char *optarg;
  76.     register char *p;
  77.     struct passwd lpw, *pw;
  78.     struct rlimit rlim;
  79.     FILE *temp_fp;
  80.     int aflag, ch, fd;
  81.     char *fend, *passwd, *temp, *tend;
  82.     char from[MAXPATHLEN], to[MAXPATHLEN];
  83.     char *getusershell();
  84.  
  85.     uid = getuid();
  86.     aflag = 0;
  87.     while ((ch = getopt(argc, argv, "a:")) != EOF)
  88.         switch(ch) {
  89.         case 'a':
  90.             if (uid) {
  91.                 (void)fprintf(stderr,
  92.                     "chpass: %s\n", strerror(EACCES));
  93.                 exit(1);
  94.             }
  95.             loadpw(optarg, pw = &lpw);
  96.             aflag = 1;
  97.             break;
  98.         case '?':
  99.         default:
  100.             usage();
  101.         }
  102.     argc -= optind;
  103.     argv += optind;
  104.  
  105.     if (!aflag)
  106.         switch(argc) {
  107.         case 0:
  108.             if (!(pw = getpwuid(uid))) {
  109.                 (void)fprintf(stderr,
  110.                     "chpass: unknown user: uid %u\n", uid);
  111.                 exit(1);
  112.             }
  113.             break;
  114.         case 1:
  115.             if (!(pw = getpwnam(*argv))) {
  116.                 (void)fprintf(stderr,
  117.                     "chpass: unknown user %s.\n", *argv);
  118.                 exit(1);
  119.             }
  120.             if (uid && uid != pw->pw_uid) {
  121.                 (void)fprintf(stderr,
  122.                     "chpass: %s\n", strerror(EACCES));
  123.                 exit(1);
  124.             }
  125.             break;
  126.         default:
  127.             usage();
  128.         }
  129.  
  130.     (void)signal(SIGHUP, SIG_IGN);
  131.     (void)signal(SIGINT, SIG_IGN);
  132.     (void)signal(SIGQUIT, SIG_IGN);
  133.     (void)signal(SIGTSTP, SIG_IGN);
  134.  
  135.     rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
  136.     (void)setrlimit(RLIMIT_CPU, &rlim);
  137.     (void)setrlimit(RLIMIT_FSIZE, &rlim);
  138.  
  139.     (void)umask(0);
  140.  
  141.     temp = _PATH_PTMP;
  142.     if ((fd = open(temp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) {
  143.         if (errno == EEXIST) {
  144.             (void)fprintf(stderr,
  145.                 "chpass: password file busy -- try again later.\n");
  146.             exit(1);
  147.         }
  148.         (void)fprintf(stderr, "chpass: %s: %s; ",
  149.             temp, strerror(errno));
  150.         goto bad;
  151.     }
  152.     if (!(temp_fp = fdopen(fd, "w"))) {
  153.         (void)fprintf(stderr, "chpass: can't write %s; ", temp);
  154.         goto bad;
  155.     }
  156.  
  157.     if (!aflag && !info(pw))
  158.         goto bad;
  159.  
  160.     /* root should have a 0 uid and a reasonable shell */
  161.     if (!strcmp(pw->pw_name, "root")) {
  162.         if (pw->pw_uid) {
  163.             (void)fprintf(stderr, "chpass: root uid should be 0.");
  164.             exit(1);
  165.         }
  166.         setusershell();
  167.         for (;;)
  168.             if (!(p = getusershell())) {
  169.                 (void)fprintf(stderr,
  170.                     "chpass: warning, unknown root shell.");
  171.                 break;
  172.             }
  173.             else if (!strcmp(pw->pw_shell, p))
  174.                 break;
  175.     }
  176.  
  177.     passwd = _PATH_MASTERPASSWD;
  178.     if (!freopen(passwd, "r", stdin)) {
  179.         (void)fprintf(stderr, "chpass: can't read %s; ", passwd);
  180.         goto bad;
  181.     }
  182.     if (!copy(pw, temp_fp))
  183.         goto bad;
  184.  
  185.     (void)fclose(temp_fp);
  186.     (void)fclose(stdin);
  187.  
  188.     switch(fork()) {
  189.     case 0:
  190.         break;
  191.     case -1:
  192.         (void)fprintf(stderr, "chpass: can't fork; ");
  193.         goto bad;
  194.         /* NOTREACHED */
  195.     default:
  196.         exit(0);
  197.         /* NOTREACHED */
  198.     }
  199.  
  200.     if (makedb(temp)) {
  201.         (void)fprintf(stderr, "chpass: mkpasswd failed; ");
  202. bad:        (void)fprintf(stderr, "%s unchanged.\n", _PATH_MASTERPASSWD);
  203.         (void)unlink(temp);
  204.         exit(1);
  205.     }
  206.  
  207.     /*
  208.      * possible race; have to rename four files, and someone could slip
  209.      * in between them.  LOCK_EX and rename the ``passwd.dir'' file first
  210.      * so that getpwent(3) can't slip in; the lock should never fail and
  211.      * it's unclear what to do if it does.  Rename ``ptmp'' last so that
  212.      * passwd/vipw/chpass can't slip in.
  213.      */
  214.     (void)setpriority(PRIO_PROCESS, 0, -20);
  215.     fend = strcpy(from, temp) + strlen(temp);
  216.     tend = strcpy(to, _PATH_PASSWD) + strlen(_PATH_PASSWD);
  217.     bcopy(".dir", fend, 5);
  218.     bcopy(".dir", tend, 5);
  219.     if ((fd = open(from, O_RDONLY, 0)) >= 0)
  220.         (void)flock(fd, LOCK_EX);
  221.     /* here we go... */
  222.     (void)rename(from, to);
  223.     bcopy(".pag", fend, 5);
  224.     bcopy(".pag", tend, 5);
  225.     (void)rename(from, to);
  226.     bcopy(".orig", fend, 6);
  227.     (void)rename(from, _PATH_PASSWD);
  228.     (void)rename(temp, passwd);
  229.     /* done! */
  230.     exit(0);
  231. }
  232.  
  233. char tfile[] = "/tmp/passwd.XXXXXX";
  234.  
  235. info(pw)
  236.     struct passwd *pw;
  237. {
  238.     struct stat begin, end;
  239.     FILE *fp;
  240.     int fd, rval;
  241.  
  242.     if ((fd = mkstemp(tfile)) == -1 || !(fp = fdopen(fd, "w+"))) {
  243.         (void)fprintf(stderr, "chpass: no temporary file");
  244.         return(0);
  245.     }
  246.  
  247.     print(fp, pw);
  248.     (void)fflush(fp);
  249.  
  250.     /*
  251.      * give the file to the real user; setuid permissions
  252.      * are discarded in edit()
  253.      */
  254.     (void)fchown(fd, getuid(), getgid());
  255.  
  256.     for (rval = 0;;) {
  257.         (void)fstat(fd, &begin);
  258.         if (edit(tfile)) {
  259.             (void)fprintf(stderr, "chpass: edit failed; ");
  260.             break;
  261.         }
  262.         (void)fstat(fd, &end);
  263.         if (begin.st_mtime == end.st_mtime) {
  264.             (void)fprintf(stderr, "chpass: no changes made; ");
  265.             break;
  266.         }
  267.         (void)rewind(fp);
  268.         if (check(fp, pw)) {
  269.             rval = 1;
  270.             break;
  271.         }
  272.         (void)fflush(stderr);
  273.         if (prompt())
  274.             break;
  275.     }
  276.     (void)fclose(fp);
  277.     (void)unlink(tfile);
  278.     return(rval);
  279. }
  280.  
  281. check(fp, pw)
  282.     FILE *fp;
  283.     struct passwd *pw;
  284. {
  285.     register struct entry *ep;
  286.     register char *p;
  287.     static char buf[1024];
  288.  
  289.     while (fgets(buf, sizeof(buf), fp)) {
  290.         if (!buf[0] || buf[0] == '#')
  291.             continue;
  292.         if (!(p = index(buf, '\n'))) {
  293.             (void)fprintf(stderr, "chpass: line too long.\n");
  294.             return(0);
  295.         }
  296.         *p = '\0';
  297.         for (ep = list;; ++ep) {
  298.             if (!ep->prompt) {
  299.                 (void)fprintf(stderr,
  300.                     "chpass: unrecognized field.\n");
  301.                 return(0);
  302.             }
  303.             if (!strncasecmp(buf, ep->prompt, ep->len)) {
  304.                 if (ep->restricted && uid)
  305.                     break;
  306.                 if (!(p = index(buf, ':'))) {
  307.                     (void)fprintf(stderr,
  308.                         "chpass: line corrupted.\n");
  309.                     return(0);
  310.                 }
  311.                 while (isspace(*++p));
  312.                 if (ep->except && strpbrk(p, ep->except)) {
  313.                     (void)fprintf(stderr,
  314.                        "chpass: illegal character in the \"%s\" field.\n",
  315.                         ep->prompt);
  316.                     return(0);
  317.                 }
  318.                 if ((ep->func)(p, pw, ep))
  319.                     return(0);
  320.                 break;
  321.             }
  322.         }
  323.     }
  324.     /*
  325.      * special checks...
  326.      *
  327.      * there has to be a limit on the size of the gecos fields,
  328.      * otherwise getpwent(3) can choke.
  329.      * ``if I swallow anything evil, put your fingers down my throat...''
  330.      *    -- The Who
  331.      */
  332.     if (strlen(list[E_NAME].save) + strlen(list[E_BPHONE].save) +
  333.         strlen(list[E_HPHONE].save) + strlen(list[E_LOCATE].save)
  334.         > 512) {
  335.         (void)fprintf(stderr, "chpass: gecos field too large.\n");
  336.         exit(1);
  337.     }
  338.     (void)sprintf(pw->pw_gecos = buf, "%s,%s,%s,%s",
  339.         list[E_NAME].save, list[E_LOCATE].save, list[E_BPHONE].save,
  340.         list[E_HPHONE].save);
  341.     return(1);
  342. }
  343.  
  344. copy(pw, fp)
  345.     struct passwd *pw;
  346.     FILE *fp;
  347. {
  348.     register int done;
  349.     register char *p;
  350.     char buf[1024];
  351.  
  352.     for (done = 0; fgets(buf, sizeof(buf), stdin);) {
  353.         /* skip lines that are too big */
  354.         if (!index(buf, '\n')) {
  355.             (void)fprintf(stderr, "chpass: line too long; ");
  356.             return(0);
  357.         }
  358.         if (done) {
  359.             (void)fprintf(fp, "%s", buf);
  360.             continue;
  361.         }
  362.         if (!(p = index(buf, ':'))) {
  363.             (void)fprintf(stderr, "chpass: corrupted entry; ");
  364.             return(0);
  365.         }
  366.         *p = '\0';
  367.         if (strcmp(buf, pw->pw_name)) {
  368.             *p = ':';
  369.             (void)fprintf(fp, "%s", buf);
  370.             continue;
  371.         }
  372.         (void)fprintf(fp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
  373.             pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
  374.             pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos,
  375.             pw->pw_dir, pw->pw_shell);
  376.         done = 1;
  377.     }
  378.     if (!done)
  379.         (void)fprintf(fp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
  380.             pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
  381.             pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos,
  382.             pw->pw_dir, pw->pw_shell);
  383.     return(1);
  384. }
  385.  
  386. makedb(file)
  387.     char *file;
  388. {
  389.     int status, pid, w;
  390.  
  391.     if (!(pid = vfork())) {
  392.         execl(_PATH_MKPASSWD, "mkpasswd", "-p", file, NULL);
  393.         _exit(127);
  394.     }
  395.     while ((w = wait(&status)) != pid && w != -1);
  396.     return(w == -1 || status);
  397. }
  398.  
  399. edit(file)
  400.     char *file;
  401. {
  402.     int status, pid, w;
  403.     char *p, *editor, *getenv();
  404.  
  405.     if (editor = getenv("EDITOR")) {
  406.         if (p = rindex(editor, '/'))
  407.             ++p;
  408.         else
  409.             p = editor;
  410.     }
  411.     else
  412.         p = editor = "vi";
  413.     if (!(pid = vfork())) {
  414.         (void)setgid(getgid());
  415.         (void)setuid(getuid());
  416. #ifdef sprite
  417.                 if (strcmp(p, "mx") == 0) {
  418.                     execlp(editor, p, "-D", file, NULL);
  419.                 } else {
  420.                     execlp(editor, p, file, NULL);
  421.                 }
  422. #else
  423.                 execlp(editor, p, file, NULL);
  424. #endif
  425.         _exit(127);
  426.     }
  427.     while ((w = wait(&status)) != pid && w != -1);
  428.     return(w == -1 || status);
  429. }
  430.  
  431. loadpw(arg, pw)
  432.     char *arg;
  433.     register struct passwd *pw;
  434. {
  435.     register char *cp;
  436.     long atol();
  437.     char *strsep();
  438.  
  439.     pw->pw_name = strsep(arg, ":");
  440.     pw->pw_passwd = strsep((char *)NULL, ":");
  441.     if (!(cp = strsep((char *)NULL, ":")))
  442.         goto bad;
  443.     pw->pw_uid = atoi(cp);
  444.     if (!(cp = strsep((char *)NULL, ":")))
  445.         goto bad;
  446.     pw->pw_gid = atoi(cp);
  447.     pw->pw_class = strsep((char *)NULL, ":");
  448.     if (!(cp = strsep((char *)NULL, ":")))
  449.         goto bad;
  450.     pw->pw_change = atol(cp);
  451.     if (!(cp = strsep((char *)NULL, ":")))
  452.         goto bad;
  453.     pw->pw_expire = atol(cp);
  454.     pw->pw_gecos = strsep((char *)NULL, ":");
  455.     pw->pw_dir = strsep((char *)NULL, ":");
  456.     pw->pw_shell = strsep((char *)NULL, ":");
  457.     if (!pw->pw_shell || strsep((char *)NULL, ":")) {
  458. bad:        (void)fprintf(stderr, "chpass: bad password list.\n");
  459.         exit(1);
  460.     }
  461. }
  462.  
  463. prompt()
  464. {
  465.     register int c;
  466.  
  467.     for (;;) {
  468.         (void)printf("re-edit the password file? [y]: ");
  469.         (void)fflush(stdout);
  470.         c = getchar();
  471.         if (c != EOF && c != (int)'\n')
  472.             while (getchar() != (int)'\n');
  473.         return(c == (int)'n');
  474.     }
  475.     /* NOTREACHED */
  476. }
  477.  
  478. usage()
  479. {
  480.     (void)fprintf(stderr, "usage: chpass [-a list] [user]\n");
  481.     exit(1);
  482. }
  483.